package paxos

import (
	"fmt"
	"log"
	"net"
	"net/rpc"
)

type Learner struct {
	lis         net.Listener
	id          int             // 学习者id
	acceptedMsg map[int]MsgArgs // 接收者已接受的提案：[提案者id]请求消息
}

// Learn 学习者学习方法，持续更新acceptedMsg保证提案一直为最新
func (l *Learner) Learn(args *MsgArgs, reply *MsgReply) error {
	oldMsg := l.acceptedMsg[args.From]
	if args.Number > oldMsg.Number {
		l.acceptedMsg[args.From] = *args
		reply.Ok = true
	} else {
		reply.Ok = false
	}
	return nil
}

// Choose 选择方法，找出被批准的值
func (l *Learner) Choose() interface{} {
	acceptCount := make(map[int]int)
	acceptMsg := make(map[int]MsgArgs)
	// 统计每个提案的数量，并创建提案和对应消息的映射
	for _, msg := range l.acceptedMsg {
		if msg.Number != 0 {
			acceptCount[msg.Number]++
			acceptMsg[msg.Number] = msg
		}
	}
	// 如果某个提案被多数节点接受了，那么该提案被批准了
	for number, count := range acceptCount {
		if count > l.majority() {
			return acceptMsg[number].Value
		}
	}
	return nil
}

// 大多数节点的数量计算方法
func (l *Learner) majority() int {
	return len(l.acceptedMsg)/2 + 1
}

func newLearner(id int, acceptorIds []int) *Learner {
	learner := &Learner{
		id:          id,
		acceptedMsg: make(map[int]MsgArgs),
	}
	learner.server(id)
	return learner
}

func (l *Learner) server(id int) {
	rpcs := rpc.NewServer()
	rpcs.Register(l)
	addr := fmt.Sprintf(":%d", id)
	lis, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatal("listen error:", err)
	}
	l.lis = lis
	go func() {
		for {
			conn, err := l.lis.Accept()
			if err != nil {
				continue
			}
			go rpcs.ServeConn(conn)
		}
	}()
}

func (l *Learner) close() {
	l.lis.Close()
}
